'use strict';
const path = require('path');
const fs = require('fs');
const fse = require('fs-extra')
const inquirer = require('inquirer');
const semver = require('semver')
const userHome = require('user-home')
const glob = require('glob')
const ejs = require('ejs')
const Command = require('@zmz-cli-dev/command');
const log = require('@zmz-cli-dev/log');
const { spinnerStart, sleep, execSync } = require('@zmz-cli-dev/utils');
const Package = require('@zmz-cli-dev/package');
const { getProjectTemplate } = require('./getProjectTemplate')
const localPath = process.cwd(); // 当前命令执行的路径

class initCommand extends Command {
  init() {
    this.projectName = this._argv[0] || '';
    this._argv.find(item => {
      if(item && item.force){
        this.force = true
      }else{
        this.force = false
      }
    })
  }
  async exec() {
    try {
      // 1、准备阶段
      const projectInfo = await this.prepare();
      this.projectInfo = projectInfo
      // 2、下载模板到用户本地的缓存目录当中
      await this.downLoadTemplate()
      // 3、安装模版
      await this.installTemplate()
    } catch (err) {
      log.error(err.message)
    }
  }
  async prepare() {
    // 0、判断脚手架模板是否存在
    const template = await getProjectTemplate() // 调用服务端接口获取模板列表
    if(!template || !template.length){
      throw new Error('当前没有模板可以下载')
    }
    this.template = template
    // 1、判断当前目录是否为空
    const ret = this.isCwdEmpty();
    if (!ret) { // 如果不为空, 询问是否继续创建
      let isContinue = false
      if(!this.force){ // 是否强制初始化项目
        isContinue = (await inquirer.prompt({
          type: 'confirm',
          name: 'isContinue',
          default: false,
          message: '当前文件夹不为空，是否继续创建？',
        })).isContinue;
        if(!isContinue) return // 退出
      }
      // 2、是否启动强制更新
      if (isContinue || this.force) {
        const { confirmDelete } = await inquirer.prompt({
          type: 'confirm',
          name: 'confirmDelete',
          default: false,
          message: '是否确认清空当前目录下的文件？'
        })
        if(confirmDelete){
          // 清空当前文件夹
          fse.emptyDirSync(localPath)
        }
      }
    }
    return this.getProjectInfo()
  }
  async downLoadTemplate() {
    const { projectTemplate } = this.projectInfo
    const templateInfo = this.template.find(item => item.npmName === projectTemplate)
    const homePath = process.env.CLI_HOME_PATH
    const targetPath = path.resolve(homePath, 'template')
    const storeDir = path.resolve(targetPath, 'node_modules')
    const templatePkg = new Package({
      targetPath,
      storeDir,
      packageName: templateInfo.npmName,
      packageVersion: templateInfo.version
    })
    if(await templatePkg.exists()) { // 如果包存在，则更新pkg
      const spinner = spinnerStart('正在更新模板...')
      await sleep()
      try {
        await templatePkg.update()
      } catch (error) {
        throw error
      } finally {
        spinner.stop(true)
        if(await templatePkg.exists()){
          log.success('模板更新成功')
          this.templatePkg = templatePkg
        }
      }
    }else{ // 如果包不存在，则安装pkg
      const spinner = spinnerStart('正在下载模板...')
      await sleep()
      try {
        await templatePkg.install()
      } catch (error) {
        throw error
      } finally {
        spinner.stop(true)
        if(await templatePkg.exists()){
          log.success('模板下载成功')
          this.templatePkg = templatePkg
        }
      }
    }
    this.templateInfo = templateInfo
  }
  async installTemplate() {
    if(!this.templateInfo){
      throw new Error('项目模板信息不存在')
    }
    if(this.templateInfo && !this.templateInfo.type){
      this.templateInfo.type = 'normal'
    }
    const { type } = this.templateInfo
    if(type === 'normal'){
      // 普通模板
      await this.installNormalTemplate()
    }else if(type === 'custom'){
      // 自定义模版
      await this.installCustomTemplate()
    }else{
      throw new Error('无法识别项目模版类型')
    }
  }
  isCwdEmpty() {
    let fileList = fs.readdirSync(localPath);
    fileList = fileList.filter(file => !file.startsWith('.') && !['node_modules'].includes(file))
    return !fileList || !fileList.length
  }
  createTemplateChoices(){
    return this.template.map(item => {
      return {
        value: item.npmName,
        name: item.name
      }
    }) 
  }
  checkCmd(cmd){
    return ['npm', 'cnpm', 'yarn', 'pnpm'].includes(cmd) ? cmd : null
  }
  isValidateProjectName(v){
    return /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(v)
  }
  async getProjectInfo() {
    const self = this
    let projectInfo = {}
    let isProjectNameValidate = false
    if(this.isValidateProjectName(this.projectName)){
      isProjectNameValidate = true
    }else{
      isProjectNameValidate = false
    }
    // 3、选择创建项目还是组件
    const { type } = await inquirer.prompt({
      type: 'list',
      name: 'type',
      default: 'project',
      message: '请选择初始化类型',
      choices: [
        {
          name: '项目',
          value: 'project',
        },
        {
          name: '组件',
          value: 'component',
        }
      ]
    })
    this.template = this.template.filter(item => item.tag.includes(type))
    const projectNameMsg = type === 'project' ? `请输入项目名称` : '请输入组件名称'
    const projectVersionMsg = type === 'project' ? `请输入项目版本号` : '请输入组件版本号'
    const projectTemplateMsg = type === 'project' ? `请选择项目模版` : '请选择组件模版'
    const projectNamePrompt = {
      type: 'input',
      name: 'projectName',
      message: projectNameMsg,
      default: '',
      validate(v) {
        // 1、首字符必须为英文字符
        // 2、尾字符必须为英文字符或数字
        // 3、中间字符允许-_
        const done = this.async()
        setTimeout(() => {
          if (!self.isValidateProjectName(v)) {
            done('请输入合法的项目名称')
          } else {
            done(null, true)
          }
        }, 0)
      },
      filter(v) {
        return v
      }
    }
    let projectPrompt = []
    if (!isProjectNameValidate) {
      projectPrompt.push(projectNamePrompt)
    }
    projectPrompt.push(
      {
        type: 'input',
        name: 'projectVersion',
        message: projectVersionMsg,
        default: '1.0.0',
        validate(v) {
          const done = this.async()
          setTimeout(() => {
            if (semver.valid(v) === null) {
              done('请输入合法的项目版本号')
            } else {
              done(null, true)
            }
          }, 0)
        },
        filter(v) {
          if (semver.valid(v) !== null) {
            return semver.valid(v)
          }
          return v
        }
      },
      {
        type: 'list',
        name: 'projectTemplate',
        message: projectTemplateMsg,
        choices: this.createTemplateChoices()
      }
    )
    if (type === 'project') {
      // 4、获取项目的基本信息
      const project = await inquirer.prompt(projectPrompt)
      projectInfo = {
        type,
        ...project,
        projectName: project.projectName || this.projectName
      }
    } else if (type === 'component') {
      const descriptionPrompt = {
        type: 'input',
        name: 'componentDescription',
        message: '请输入组件描述信息',
        default: '',
        validate(v) {
          const done = this.async()
          setTimeout(() => {
            if (!v) {
              done('请输入合法的项目版本号')
            } else {
              done(null, true)
            }
          }, 0)
        },
      }
      projectPrompt.push(descriptionPrompt)
      // 4、获取组件的基本信息
      const project = await inquirer.prompt(projectPrompt)
      projectInfo = {
        type,
        ...project,
        projectName: project.projectName || this.projectName
      }
    }
    if(projectInfo.projectName){
      projectInfo.projectClassName = require('kebab-case')(projectInfo.projectName).replace(/\^-/g, '')
    }
    console.log('projectInfo', projectInfo);
    return projectInfo
  }
  async execCommand(command, msg = '执行命令失败'){
    if(command){
      const cmdArr = command.split(' ')
      const cmd = this.checkCmd(cmdArr[0])
      if(!cmd){
        throw new Error('无法识别命令: ' + command)
      }
      const args = cmdArr.slice(1)
      const res = await execSync(cmd, args, {
        cwd: process.cwd(), // 当前命令执行的路径
        stdio: 'inherit' // 子进程的输出流，会在主进程中显示信息
      })
      if(res === 0){ // 0 表示依赖安装成功
        return res
      }else{
        throw new Error(msg)
      }
    }
  }
  async ejsRender(ignore){
    return new Promise((resolve, reject) => {
      glob('**', {
        cwd: localPath,
        ignore,
        nodir: true
      }, (err, files) => {
        if(err){
          reject(err)
        }
        Promise.all(files.map(file => {
          const filePath = path.join(localPath, file)
          return new Promise((resolve1, reject1) => {
            ejs.renderFile(filePath, this.projectInfo, {}, (err, res) => {
              if(err){
                reject1(err)
              }else{
                fse.writeFileSync(filePath, res)
                resolve1(res)
              }
            })
          })
        })).then(() => {
          resolve()
        }).catch(err => {
          reject(err)
        })
      })
    })
  }
  async installNormalTemplate(){
    // 拷贝模版代码至当前目录
    const spinner = spinnerStart('正在安装模板...')
    await sleep()
    try {
      const templatePath = path.resolve(this.templatePkg.cacheFilePath, 'template')
      const targetPath = process.cwd()
      fse.ensureDirSync(templatePath) // 创建目录
      fse.ensureDirSync(targetPath)
      fse.copySync(templatePath, targetPath) // 拷贝
    } catch (error) {
      throw error
    } finally {
      spinner.stop(true)
      log.success('模板安装成功')
    }
    const templateIgnore = this.templateInfo.ignore || []
    const ignore = ['**/node_modules/**', ...templateIgnore]
    await this.ejsRender(ignore)
    const { installCommand, startCommand} = this.templateInfo
    // 安装依赖
    await this.execCommand(installCommand, '依赖安装失败')
    // 执行启动命令
    await this.execCommand(startCommand, '项目启动失败')
  }
  async installCustomTemplate(){}
}
function init(argv) {
  return new initCommand(argv);
}

module.exports = init;

